#include "toassembler.h"

void print_op_equal(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	fprintf(fout,"\tbqe $t1, $t2, label%d\n",q->next);
}

void print_op_nequal(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	fprintf(fout,"\tbne $t1, $t2, label%d\n",q->next);
}

void print_op_inf(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	fprintf(fout,"\tblt $t1, $t2, label%d\n",q->next);
}

void print_op_inf_equal(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	fprintf(fout,"\tble $t1, $t2, label%d\n",q->next);
}

void print_op_sup(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	fprintf(fout,"\tbgt $t1, $t2, label%d\n",q->next);
}

void print_op_sup_equal(FILE* fout,quad *q){	
	fprintf(fout,"\nlabel%d:\n",q->label);
	if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	fprintf(fout,"\tbge $t1, $t2, label%d\n",q->next);
}

void print_op_assign(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tla $t0, _%s\n",q->res->nom);
	if(q->arg1->type==TYPE_CONSTANT) fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
	else fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
	fprintf(fout,"\tsw $t1, ($t0)\n");
}

void print_op_assign_ptr(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tlw $t0, _%s\n",q->res->nom);
	if(q->arg1->type==TYPE_CONSTANT) fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
	else fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
	fprintf(fout,"\tsw $t1, ($t0)\n");
}

void print_op_assign_adr(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tla $t0, _%s\n",q->res->nom);
	fprintf(fout,"\tla $t1, _%s\n",q->arg1->nom);
	fprintf(fout,"\tsw $t1, ($t0)\n");
}

void print_op_assign_val(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tla $t0, _%s\n",q->res->nom);
	fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
	fprintf(fout,"\tlw $t2, ($t1)\n");
	fprintf(fout,"\tsw $t2, ($t0)\n");
}

void print_op_add(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tla $t0, _%s\n",q->res->nom);
	if(q->arg1->type==TYPE_CONSTANT && q->arg2->type==TYPE_CONSTANT) {
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg2->nom);
		fprintf(fout,"\taddi $t1, $t1, %d\n",q->arg1->valeur);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\taddi $t1, $t1, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
		fprintf(fout,"\tadd $t1, $t1, $t2\n");
	}
	fprintf(fout,"\tsw $t1, ($t0)\n");
}

void print_op_sub(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tla $t0, _%s\n",q->res->nom);
	fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
	fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	fprintf(fout,"\tsubu $t1, $t1, $t2\n");
	fprintf(fout,"\tsw $t1, ($t0)\n");
}

void print_op_abs(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tla $t2, _%s\n",q->res->nom);
	fprintf(fout,"\tlw $t0, _%s\n",q->arg1->nom);
	fprintf(fout,"\tsra $t1,$t0,31\n");   
 	fprintf(fout,"\txor $t0,$t0,$t1\n");   
	fprintf(fout,"\tsub $t0,$t0,$t1\n"); 
	fprintf(fout,"\tsw $t0, ($t2)\n");
}

void print_op_mul(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tla $t0, _%s\n",q->res->nom);
	if(q->arg1->type==TYPE_CONSTANT && q->arg2->type==TYPE_CONSTANT) {
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	fprintf(fout,"\tmul $t1, $t1, $t2\n");
	fprintf(fout,"\tsw $t1, ($t0)\n");
}

void print_op_div(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tla $t0, _%s\n",q->res->nom);
	if(q->arg1->type==TYPE_CONSTANT && q->arg2->type==TYPE_CONSTANT) {
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else if(q->arg1->type==TYPE_CONSTANT){
		fprintf(fout,"\tli $t1, %d\n",q->arg1->valeur);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	else if(q->arg2->type==TYPE_CONSTANT){
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tli $t2, %d\n",q->arg2->valeur);
	}
	else{
		fprintf(fout,"\tlw $t1, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $t2, _%s\n",q->arg2->nom);
	}
	fprintf(fout,"\tdiv $t1, $t1, $t2\n");
	fprintf(fout,"\tsw $t1, ($t0)\n");
}

void print_op_goto(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tj label%d\n",q->next);
}

void print_label_end(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tli $v0, 10\n");
	fprintf(fout,"\tsyscall\n");
}

void print_op_print_digit(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tli $v0, 1\n");
	if(q->arg1->type == TYPE_ARRAY) {
		fprintf(fout,"\tlw $t0, _%s\n",q->arg1->nom);
		fprintf(fout,"\tlw $a0, ($t0)\n");
	}
	else fprintf(fout,"\tlw $a0, _%s\n",q->arg1->nom);
	fprintf(fout,"\tsyscall\n");
}

void print_op_print_string(FILE* fout,quad *q){
	fprintf(fout,"\nlabel%d:\n",q->label);
	fprintf(fout,"\tli $v0, 4\n");
	fprintf(fout,"\tla $a0, _%s\n",q->arg1->nom);
	fprintf(fout,"\tsyscall\n");
}

void printassembler(FILE* fout,list_quad *l, symbol_table *st){
	fprintf(fout,".data\n");
	while(st != NULL){
		if(st->type == TYPE_STRING)fprintf(fout,"_%s : \t\t.asciiz \t%s\n",st->nom,st->texte);
		else if(st->type == TYPE_ARRAY && st->dim != NULL){
			 fprintf(fout,"_%s : \t\t.word 0",st->nom);
			 int i = get_size(st->dim);
			 int j;
			 for(j=0;j<i-1;j++)fprintf(fout,",0");
			 fprintf(fout,"\n");
		}
		else fprintf(fout,"_%s : \t\t.word 0\n",st->nom);
		
		
		st = st->suivant;
	}
	fprintf(fout,"\n.text	\n.globl main \nmain:\n");
	while(l != NULL){
		switch(l->q->operateur){
			case OP_EQUAL : print_op_equal(fout,l->q); break;
			case OP_NEQUAL : print_op_nequal(fout,l->q); break;
			case OP_INF : print_op_inf(fout,l->q); break;
			case OP_INF_EQUAL : print_op_inf_equal(fout,l->q); break;
			case OP_SUP : print_op_sup(fout,l->q); break;		
			case OP_SUP_EQUAL : print_op_sup_equal(fout,l->q); break;
			case OP_ASSIGN : print_op_assign(fout,l->q); break;
			case OP_ASSIGN_ADR : print_op_assign_adr(fout,l->q); break;
			case OP_ASSIGN_PTR : print_op_assign_ptr(fout,l->q); break;
			case OP_ASSIGN_VAL : print_op_assign_val(fout,l->q); break;	
			case OP_ADD : print_op_add(fout,l->q); break;		
			case OP_SUB : print_op_sub(fout,l->q); break;		
			case OP_MUL : print_op_mul(fout,l->q); break;			
			case OP_DIV : print_op_div(fout,l->q); break;
			case OP_ABS : print_op_abs(fout,l->q); break;	
			case NUL_QUAD : break;
			case OP_PRINT_DIGIT : print_op_print_digit(fout,l->q); break;
			case OP_PRINT_STRING : print_op_print_string(fout,l->q); break;		
			case OP_GOTO : print_op_goto(fout,l->q); break;
			case LABEL_END : print_label_end(fout,l->q); break;
		}
		l = l->next;
	}
}
